#!/usr/bin/env python
# _*_ coding: utf-8 _*_
"""
@author: caimengzhi
@license: (C) Copyright 2013-2017.
@contact: 610658552@qq.com
@software: pycharm 2017.02
@project: luxffy
@file: server_start.py
@time: 18-3-2 上午9:52
@desc:
"""
import optparse
import socket
import json

class FTPClient(object):
    MSG_SIZE = 1024
    STATUS_CODE = {
        200: "Passed authentication!",
        201: "Wrong username or password!",
        300: "File does not exist !",
        301: "File exist , and this msg include the file size- !",
        302: "This msg include the msg size!",
        350: "Dir changed !",
        351: "Dir doesn't exist !",
        401: "File exist ,ready to re-send !",
        402: "File exist ,but file size doesn't match!",
    }

    def __init__(self):
        self.username = None
        parser = optparse.OptionParser()
        parser.add_option("-s","--server",dest='server',help="ftp server ip_addr")
        parser.add_option("-P","--port",type='int',help="ftp server ip_port")
        parser.add_option("-u","--username",dest='username',help="username info")
        parser.add_option("-p","--password",dest='password',help="password info")
        self.options, self.args = parser.parse_args()

        self.argv_verification()
        self.make_connection()

    def argv_verification(self):
        if not self.options.server or not self.options.port:
            print("Error: must supply server and port parameters")

    def make_connection(self):
        self.sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.sock.connect((self.options.server,self.options.port))

    def get_response(self):
        data = self.sock.recv(self.MSG_SIZE)
        return json.loads(data.decode("utf-8"))

    def auth(self):
        count = 0
        while count < 3:
            username = input("username: ").strip()
            if not username: continue
            password = input("passowrd: ").strip()
            cmd = {
                "action_type":'auth',
                'username': username,
                'password': password
            }
            self.sock.send(json.dumps(cmd).encode('utf-8'))
            # print("send username and password and recv")
            response = self.get_response()
            print("respone = ",response)
            if response.get("status_code") == 200:  # pass auth
                self.username = username
                return True
            elif response.get("status_code") == 500:
                exit("he client from server of number is max.pls wait a m")
            else:
                print(response.get("status_msg"))   # not pass auth and print status-msg
            count += 1
        print("sorry. try too many time. pls wait...")

    def interactive(self):

        if self.auth():
            while True:
                user_input = input("[%s]>: "%self.username).strip()
                if not user_input: continue

                cmd_list = user_input.split()
                if hasattr(self, "_%s" % cmd_list[0]):
                    getattr(self,"_%s" % cmd_list[0])(cmd_list[1:])
                else:
                    print("sorry your command is not support.pls input help")

    def send_msg(self,action_type, **kwargs):
        msg_data = {
            "action_type": action_type,
            "fill": ""
        }
        msg_data.update(kwargs)
        bytes_msg = json.dumps(msg_data).encode('utf-8')
        if len(bytes_msg) > self.MSG_SIZE:
            msg_data['fill'] = msg_data['fill'].zfill(self.MSG_SIZE - len(bytes_msg))
            bytes_msg = json.dumps(msg_data).encode('utf-8')

        self.sock.send(bytes_msg)

    def _help(self,cmd_args):
        msg = """
        get file  ---> download from remote file to local
        put file  ---> upload local exist file to remote server
        ls        ---> list remote server file and directions
        cd        ---> change your direction at remote server
        """
        print(msg)

    def parameter_check(self,args, min_args=None, max_args=None, exact_args=None):
        if min_args:
            if len(args) < min_args:
                print("must provide at least %s paraneters bur %s recevied" % (min_args, len(min_args)))
                return False

        if max_args:
            if len(args) > max_args:
                print("must provide at least %s paraneters bur %s recevied" % (max_args, len(max_args)))
                return False

        if exact_args:
            if len(args) != exact_args:
                print("must provide at least %s paraneters bur %s recevied" % (exact_args, len(exact_args)))
                return False

        return True

    def _get(self,cmd_args):
        """
        1. get filename
        2. send to sever
        3. respone from server
            1. the file exist
                recv the file from server
            2. the file not exist
                print(status_msg)
        :param cmd_args: ex--> get 1.mp4
        :return:
        """
        print("start download file from server")
        print(cmd_args)
        if self.parameter_check(cmd_args, min_args=1):
            filename = cmd_args[0]  # get filename
            self.send_msg(action_type="get", filename=filename)
            response = self.get_response()
            # print(response)
            if response.get("status_code") == 301:  # file exist
                file_size = response.get("file_size")
                received_size = 0

                # start receive the server file
                with open(filename, "wb") as fd:
                    while received_size < file_size:
                        if file_size - received_size < self.MSG_SIZE: # last recv
                            data = self.sock.recv(file_size - received_size)
                        else:
                            data = self.sock.recv(self.MSG_SIZE)
                        received_size += len(data)
                        fd.write(data)
                        print(received_size,file_size)
                    else:
                        print("---file [%s] recv done,received size [%s]----" % (filename, file_size))

            elif response.get("status_code") == 300: # file not exist
                print("--->",response.get("status_msg"))

    def _ls(self, cmd_args):
        """
        display current dir's or ls file list
        :param cmd_args:
        :return:
        """
        self.send_msg(action_type="ls")
        response = self.get_response()
        # print("ls send over and recv from server response = ", response)
        if response.get("status_code") == 302:  # read to recv
            cmd_result_size = response.get("cmd_result_size")
            received_size = 0
            cmd_result = b""
            while received_size < cmd_result_size:
                if cmd_result_size - received_size < self.MSG_SIZE: # last recv lt MSG_SIZE
                    data = self.sock.recv(cmd_result_size - received_size)
                else:
                    data = self.sock.recv(self.MSG_SIZE)
                cmd_result += data
                received_size += len(data)
            else:
                print(cmd_result.decode("utf-8"))

    def _cd(self, cmd_args):
        """
        change to target dir
        :param cmd_args:
        :return:
        """
        if self.parameter_check(cmd_args,min_args=1):
            target_dir = cmd_args[0]
            self.send_msg(action_type="cd",target_dir=target_dir)
            response = self.get_response()
            # print("client have send cd ** command and recv frome sever result = ", response)
            if response.get("target_dir") == 350:  # dir change
                self.terminal_display = "[/%s]" % response.get("current_dir")
                self.current_dir = response.get("current_dir")


if __name__ == "__main__":
    client = FTPClient()
    client.interactive()  # 交互
